#include "structMesh_testFunctions.hpp"
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_approx.hpp>

using Catch::Approx;

using namespace hms;
using CellType = StructMesh::CellType;

TEST_CASE( "True structCell: number of edges", "[omp]" ){

	auto test = []( const auto& mesh ){
			
		SECTION( "nEdges" ){
			mesh.forEachDomainCell( [](const CellType& cell){
				REQUIRE(cell.nEdges() == 4);
			});
			mesh.forEachGhostCell( [](const CellType& cell){
				REQUIRE(cell.nEdges() == 1);
			});
		}
	};

	SECTION("One ghost layer"){
		hms::StructMesh mesh = createStructMesh();
		test(mesh);
	}
}

TEST_CASE( "True structCell: cell area" ){
	auto testOneLayer = [](const auto& mesh){
		
		REQUIRE( mesh.cell( 1).area() == Approx( 1    ) );
		REQUIRE( mesh.cell( 2).area() == Approx( 1.46 ) );
		REQUIRE( mesh.cell( 3).area() == Approx( 0.8  ) );

		REQUIRE( mesh.cell( 5).area() == Approx( 1    ) );
		REQUIRE( mesh.cell( 6).area() == Approx( 1 	  ) );
		REQUIRE( mesh.cell( 7).area() == Approx( 1.46 ) );
		REQUIRE( mesh.cell( 8).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell( 9).area() == Approx( 0.8  ) );

		REQUIRE( mesh.cell(10).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(11).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(12).area() == Approx( 0.32 ) );
		REQUIRE( mesh.cell(13).area() == Approx( 0.48 ) );
		REQUIRE( mesh.cell(14).area() == Approx( 0.48 ) );

		REQUIRE( mesh.cell(15).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(16).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(17).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(18).area() == Approx( 0.96 ) );
		REQUIRE( mesh.cell(19).area() == Approx( 0.96 ) );

		REQUIRE( mesh.cell(21).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(22).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(23).area() == Approx( 0.96 ) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell( 2).area() == Approx( 1    ) );
		REQUIRE( mesh.cell( 3).area() == Approx( 1.46 ) );
		REQUIRE( mesh.cell( 4).area() == Approx( 0.8  ) );

		REQUIRE( mesh.cell( 9).area() == Approx( 1    ) );
		REQUIRE( mesh.cell(10).area() == Approx( 1.46 ) );
		REQUIRE( mesh.cell(11).area() == Approx( 0.8  ) );

		REQUIRE( mesh.cell(14).area() == Approx( 1    ) );
		REQUIRE( mesh.cell(15).area() == Approx( 1    ) );
		REQUIRE( mesh.cell(16).area() == Approx( 1    ) );
		REQUIRE( mesh.cell(17).area() == Approx( 1.46 ) );
		REQUIRE( mesh.cell(18).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(19).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(20).area() == Approx( 0.8  ) );

		REQUIRE( mesh.cell(21).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(22).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(23).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(24).area() == Approx( 0.32 ) );
		REQUIRE( mesh.cell(25).area() == Approx( 0.48 ) );
		REQUIRE( mesh.cell(26).area() == Approx( 0.48 ) );
		REQUIRE( mesh.cell(27).area() == Approx( 0.48 ) );

		REQUIRE( mesh.cell(28).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(29).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(30).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(31).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(32).area() == Approx( 0.96 ) );
		REQUIRE( mesh.cell(33).area() == Approx( 0.96 ) );
		REQUIRE( mesh.cell(34).area() == Approx( 0.96 ) );

		REQUIRE( mesh.cell(37).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(38).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(39).area() == Approx( 0.96 ) );

		REQUIRE( mesh.cell(44).area() == Approx( 0.76 ) );
		REQUIRE( mesh.cell(45).area() == Approx( 0.8  ) );
		REQUIRE( mesh.cell(46).area() == Approx( 0.96 ) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE( "True structCell: circumference" ){
	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.cell( 6).circumf() == Approx(sqrt(29)  / 5  + 1.2          + 0.8          + sqrt(41) / 5) );
		REQUIRE( mesh.cell( 7).circumf() == Approx(sqrt(17)  / 5  + 1.4          + 1.2          + sqrt(58) / 5) );
		REQUIRE( mesh.cell( 8).circumf() == Approx(sqrt(101) / 10 + sqrt(5)  / 5 + sqrt(58) / 5 + sqrt( 5) / 2) );
		REQUIRE( mesh.cell(11).circumf() == Approx(sqrt(2)        + sqrt(41) / 5 + sqrt(2)  / 5 + sqrt(29) / 5) );
		REQUIRE( mesh.cell(12).circumf() == Approx(sqrt(2)   / 5  + 1.4          + sqrt(13) / 5 + sqrt(10) / 5) );
		REQUIRE( mesh.cell(13).circumf() == Approx(1.4            + 1.0          + sqrt(13) / 5 + sqrt(5)  / 5) );
		REQUIRE( mesh.cell(16).circumf() == Approx(sqrt(10)  / 5  + sqrt(29) / 5 + sqrt(26) / 5 + 1           ) );
		REQUIRE( mesh.cell(17).circumf() == Approx(sqrt(26)  / 5  + sqrt(10) / 5 + sqrt(20) / 5 + 1.2         ) );
		REQUIRE( mesh.cell(18).circumf() == Approx(sqrt(20)  / 5  + 1.4          + 0.8          + 1           ) );
	};
	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell(16).circumf() == Approx(sqrt(29)  / 5  + 1.2          + 0.8          + sqrt(41) / 5) );
		REQUIRE( mesh.cell(17).circumf() == Approx(sqrt(17)  / 5  + 1.4          + 1.2          + sqrt(58) / 5) );
		REQUIRE( mesh.cell(18).circumf() == Approx(sqrt(101) / 10 + sqrt(5)  / 5 + sqrt(58) / 5 + sqrt( 5) / 2) );
		REQUIRE( mesh.cell(23).circumf() == Approx(sqrt(2)        + sqrt(41) / 5 + sqrt(2)  / 5 + sqrt(29) / 5) );
		REQUIRE( mesh.cell(24).circumf() == Approx(sqrt(2)   / 5  + 1.4          + sqrt(13) / 5 + sqrt(10) / 5) );
		REQUIRE( mesh.cell(25).circumf() == Approx(1.4            + 1.0          + sqrt(13) / 5 + sqrt(5)  / 5) );
		REQUIRE( mesh.cell(30).circumf() == Approx(sqrt(10)  / 5  + sqrt(29) / 5 + sqrt(26) / 5 + 1           ) );
		REQUIRE( mesh.cell(31).circumf() == Approx(sqrt(26)  / 5  + sqrt(10) / 5 + sqrt(20) / 5 + 1.2         ) );
		REQUIRE( mesh.cell(32).circumf() == Approx(sqrt(20)  / 5  + 1.4          + 0.8          + 1           ) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE( "True structCell: boundary and ghost flags", "[omp]" ){
	auto test = []( const auto& mesh ){
		Index m {mesh.nCellsX()};
		Index n {mesh.nCellsY()};

		mesh.forEachDomainCell( [&](const CellType& cell){
			REQUIRE( !cell.isGhost() );
			if (cell.i()==0 || cell.i()==m-1 || cell.j()==0 || cell.j()==n-1)
				REQUIRE( cell.isBoundary() );
			else
				REQUIRE( !cell.isBoundary() );
		});

		mesh.forEachGhostCell( [&](const CellType& cell){
			REQUIRE( !cell.isBoundary() );
			REQUIRE( cell.isGhost() );
		});
	};

	SECTION("One ghost layer"){
		hms::StructMesh mesh = createStructMesh();
		test(mesh);
	}
}

TEST_CASE( "True structCell: centroids" ){
	using hms::approxEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( approxEqual( mesh.cell( 6).centroid(), Vector2s{ 0.5333333333, 0.7200000000 } ) );
		REQUIRE( approxEqual( mesh.cell( 7).centroid(), Vector2s{ 1.5799086758, 1.0018264840 } ) );
		REQUIRE( approxEqual( mesh.cell( 8).centroid(), Vector2s{ 2.3566666667, 0.6625000000 } ) );
		REQUIRE( approxEqual( mesh.cell(11).centroid(), Vector2s{ 0.4736842105, 1.5947368421 } ) );
		REQUIRE( approxEqual( mesh.cell(12).centroid(), Vector2s{ 1.6833333333, 1.7416666667 } ) );
		REQUIRE( approxEqual( mesh.cell(13).centroid(), Vector2s{ 2.5777777778, 1.7555555556 } ) );
		REQUIRE( approxEqual( mesh.cell(16).centroid(), Vector2s{ 0.6421052632, 2.3982456140 } ) );
		REQUIRE( approxEqual( mesh.cell(17).centroid(), Vector2s{ 1.5333333333, 2.4000000000 } ) );
		REQUIRE( approxEqual( mesh.cell(18).centroid(), Vector2s{ 2.5944444444, 2.3777777778 } ) );

		REQUIRE( approxEqual( mesh.cell( 1).centroid(), Vector2s{ 0.8827586207, -0.1535632184 } ) );
		REQUIRE( approxEqual( mesh.cell( 2).centroid(), Vector2s{ 1.2284716626, -0.4039215686 } ) );
		REQUIRE( approxEqual( mesh.cell( 3).centroid(), Vector2s{ 2.2540594059, -0.3635726073 } ) );

		REQUIRE( approxEqual( mesh.cell(21).centroid(), Vector2s{ 0.6421052632, 3.2017543860 } ) );
		REQUIRE( approxEqual( mesh.cell(22).centroid(), Vector2s{ 1.5333333333, 3.2000000000 } ) );
		REQUIRE( approxEqual( mesh.cell(23).centroid(), Vector2s{ 2.5944444444, 3.2222222222 } ) );

		REQUIRE( approxEqual( mesh.cell( 5).centroid(), Vector2s{ -0.5333333333,  0.7200000000 } ) );
		REQUIRE( approxEqual( mesh.cell(10).centroid(), Vector2s{ -0.2322105263,  1.6955789474 } ) );
		REQUIRE( approxEqual( mesh.cell(15).centroid(), Vector2s{ -0.2726315789,  2.0933333333 } ) );

		REQUIRE( approxEqual( mesh.cell( 9).centroid(), Vector2s{ 3.0169600000, 0.7825533333 } ) );
		REQUIRE( approxEqual( mesh.cell(14).centroid(), Vector2s{ 3.1395555556, 1.3342222222 } ) );
		REQUIRE( approxEqual( mesh.cell(19).centroid(), Vector2s{ 3.8055555556, 2.3777777778 } ) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( approxEqual( mesh.cell(16).centroid(), Vector2s{ 0.5333333333, 0.7200000000 } ) );
		REQUIRE( approxEqual( mesh.cell(17).centroid(), Vector2s{ 1.5799086758, 1.0018264840 } ) );
		REQUIRE( approxEqual( mesh.cell(18).centroid(), Vector2s{ 2.3566666667, 0.6625000000 } ) );
		REQUIRE( approxEqual( mesh.cell(23).centroid(), Vector2s{ 0.4736842105, 1.5947368421 } ) );
		REQUIRE( approxEqual( mesh.cell(24).centroid(), Vector2s{ 1.6833333333, 1.7416666667 } ) );
		REQUIRE( approxEqual( mesh.cell(25).centroid(), Vector2s{ 2.5777777778, 1.7555555556 } ) );
		REQUIRE( approxEqual( mesh.cell(30).centroid(), Vector2s{ 0.6421052632, 2.3982456140 } ) );
		REQUIRE( approxEqual( mesh.cell(31).centroid(), Vector2s{ 1.5333333333, 2.4000000000 } ) );
		REQUIRE( approxEqual( mesh.cell(32).centroid(), Vector2s{ 2.5944444444, 2.3777777778 } ) );
		
		REQUIRE( approxEqual( mesh.cell( 2).centroid(), Vector2s{ 1.2321839080, -1.0271264368 } ) );
		REQUIRE( approxEqual( mesh.cell( 9).centroid(), Vector2s{ 0.8827586207, -0.1535632184 } ) );
		REQUIRE( approxEqual( mesh.cell( 3).centroid(), Vector2s{ 0.8770346495, -1.8096696213 } ) );
		REQUIRE( approxEqual( mesh.cell(10).centroid(), Vector2s{ 1.2284716626, -0.4039215686 } ) );
		REQUIRE( approxEqual( mesh.cell( 4).centroid(), Vector2s{ 2.1514521452, -1.3896452145 } ) );
		REQUIRE( approxEqual( mesh.cell(11).centroid(), Vector2s{ 2.2540594059, -0.3635726073 } ) );

		REQUIRE( approxEqual( mesh.cell(37).centroid(), Vector2s{ 0.6421052632, 3.2017543860 } ) );
		REQUIRE( approxEqual( mesh.cell(44).centroid(), Vector2s{ 0.6421052632, 4.0052631579 } ) );
		REQUIRE( approxEqual( mesh.cell(38).centroid(), Vector2s{ 1.5333333333, 3.2000000000 } ) );
		REQUIRE( approxEqual( mesh.cell(45).centroid(), Vector2s{ 1.5333333333, 4.0000000000 } ) );
		REQUIRE( approxEqual( mesh.cell(39).centroid(), Vector2s{ 2.5944444444, 3.2222222222 } ) );
		REQUIRE( approxEqual( mesh.cell(46).centroid(), Vector2s{ 2.5944444444, 4.0666666667 } ) );

		REQUIRE( approxEqual( mesh.cell(14).centroid(), Vector2s{ -1.6000000000,  0.7200000000 } ) );
		REQUIRE( approxEqual( mesh.cell(15).centroid(), Vector2s{ -0.5333333333,  0.7200000000 } ) );
		REQUIRE( approxEqual( mesh.cell(21).centroid(), Vector2s{ -0.9381052632,  1.7964210526 } ) );
		REQUIRE( approxEqual( mesh.cell(22).centroid(), Vector2s{ -0.2322105263,  1.6955789474 } ) );
		REQUIRE( approxEqual( mesh.cell(28).centroid(), Vector2s{ -1.1873684211,  1.7884210526 } ) );
		REQUIRE( approxEqual( mesh.cell(29).centroid(), Vector2s{ -0.2726315789,  2.0933333333 } ) );

		REQUIRE( approxEqual( mesh.cell(19).centroid(), Vector2s{ 3.0169600000, 0.7825533333 } ) );
		REQUIRE( approxEqual( mesh.cell(20).centroid(), Vector2s{ 3.6772533333, 0.9026066667 } ) );
		REQUIRE( approxEqual( mesh.cell(26).centroid(), Vector2s{ 3.1395555556, 1.3342222222 } ) );
		REQUIRE( approxEqual( mesh.cell(27).centroid(), Vector2s{ 3.7013333333, 0.9128888889 } ) );
		REQUIRE( approxEqual( mesh.cell(33).centroid(), Vector2s{ 3.8055555556, 2.3777777778 } ) );
		REQUIRE( approxEqual( mesh.cell(34).centroid(), Vector2s{ 5.0166666667, 2.3777777778 } ) );
	};
	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE( "True structCell: neighbours" ){

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( (mesh.cell( 1).neighbours() ==  6 ).all() );
		REQUIRE( (mesh.cell( 2).neighbours() ==  7 ).all() );
		REQUIRE( (mesh.cell( 3).neighbours() ==  8 ).all() );

		REQUIRE( (mesh.cell( 5).neighbours() ==  6 ).all() ); 
		REQUIRE( (mesh.cell( 6).neighbours() ==  Array4i{ 1,  7, 11,  5 }).all() ); 
		REQUIRE( (mesh.cell( 7).neighbours() ==  Array4i{ 2,  8, 12,  6 }).all() ); 
		REQUIRE( (mesh.cell( 8).neighbours() ==  Array4i{ 3,  9, 13,  7 }).all() ); 
		REQUIRE( (mesh.cell( 9).neighbours() ==  8 ).all() ); 

		REQUIRE( (mesh.cell(10).neighbours() == 11 ).all() );
		REQUIRE( (mesh.cell(11).neighbours() == Array4i{  6, 12, 16, 10 }).all() );
		REQUIRE( (mesh.cell(12).neighbours() == Array4i{  7, 13, 17, 11 }).all() );
		REQUIRE( (mesh.cell(13).neighbours() == Array4i{  8, 14, 18, 12 }).all() );
		REQUIRE( (mesh.cell(14).neighbours() == 13 ).all() );

		REQUIRE( (mesh.cell(15).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(16).neighbours() == Array4i{ 11, 17, 21, 15 }).all() );
		REQUIRE( (mesh.cell(17).neighbours() == Array4i{ 12, 18, 22, 16 }).all() );
		REQUIRE( (mesh.cell(18).neighbours() == Array4i{ 13, 19, 23, 17 }).all() );
		REQUIRE( (mesh.cell(19).neighbours() == 18 ).all() );

		REQUIRE( (mesh.cell(21).neighbours() ==  16 ).all() );
		REQUIRE( (mesh.cell(22).neighbours() ==  17 ).all() );
		REQUIRE( (mesh.cell(23).neighbours() ==  18 ).all() );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( (mesh.cell( 2).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell( 3).neighbours() == 17 ).all() );
		REQUIRE( (mesh.cell( 4).neighbours() == 18 ).all() );

		REQUIRE( (mesh.cell( 9).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(10).neighbours() == 17 ).all() );
		REQUIRE( (mesh.cell(11).neighbours() == 18 ).all() );
		
		REQUIRE( (mesh.cell(14).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(15).neighbours() == 16 ).all() );
		REQUIRE( (mesh.cell(16).neighbours() == Array4i{  9, 17, 23, 15 }).all() );
		REQUIRE( (mesh.cell(17).neighbours() == Array4i{ 10, 18, 24, 16 }).all() );
		REQUIRE( (mesh.cell(18).neighbours() == Array4i{ 11, 19, 25, 17 }).all() );
		REQUIRE( (mesh.cell(19).neighbours() == 18 ).all() );
		REQUIRE( (mesh.cell(20).neighbours() == 18 ).all() );

		REQUIRE( (mesh.cell(21).neighbours() == 23 ).all() );
		REQUIRE( (mesh.cell(22).neighbours() == 23 ).all() );
		REQUIRE( (mesh.cell(23).neighbours() == Array4i{ 16, 24, 30, 22 }).all() );
		REQUIRE( (mesh.cell(24).neighbours() == Array4i{ 17, 25, 31, 23 }).all() );
		REQUIRE( (mesh.cell(25).neighbours() == Array4i{ 18, 26, 32, 24 }).all() );
		REQUIRE( (mesh.cell(26).neighbours() == 25 ).all() );
		REQUIRE( (mesh.cell(27).neighbours() == 25 ).all() );

		REQUIRE( (mesh.cell(28).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(29).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(30).neighbours() == Array4i{ 23, 31, 37, 29 }).all() );
		REQUIRE( (mesh.cell(31).neighbours() == Array4i{ 24, 32, 38, 30 }).all() );
		REQUIRE( (mesh.cell(32).neighbours() == Array4i{ 25, 33, 39, 31 }).all() );
		REQUIRE( (mesh.cell(33).neighbours() == 32 ).all() );
		REQUIRE( (mesh.cell(34).neighbours() == 32 ).all() );
		
		REQUIRE( (mesh.cell(37).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(38).neighbours() == 31 ).all() );
		REQUIRE( (mesh.cell(39).neighbours() == 32 ).all() );

		REQUIRE( (mesh.cell(44).neighbours() == 30 ).all() );
		REQUIRE( (mesh.cell(45).neighbours() == 31 ).all() );
		REQUIRE( (mesh.cell(46).neighbours() == 32 ).all() );
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE(  "True structCell: vertices" ){
	using hms::exactlyEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( exactlyEqual(mesh.cell( 1).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell( 2).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell( 3).vertices(), Array2i{  2,  3 }) );
		
		REQUIRE( exactlyEqual(mesh.cell( 5).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell( 6).vertices(), Array4i{  0,  1,  5,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell( 7).vertices(), Array4i{  1,  2,  6,  5 }) );
		REQUIRE( exactlyEqual(mesh.cell( 8).vertices(), Array4i{  2,  3,  7,  6 }) );
		REQUIRE( exactlyEqual(mesh.cell( 9).vertices(), Array2i{  3,  7 }) );

		REQUIRE( exactlyEqual(mesh.cell(10).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(11).vertices(), Array4i{  4,  5,  9,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(12).vertices(), Array4i{  5,  6, 10,  9 }) );
		REQUIRE( exactlyEqual(mesh.cell(13).vertices(), Array4i{  6,  7, 11, 10 }) );
		REQUIRE( exactlyEqual(mesh.cell(14).vertices(), Array2i{  7, 11 }) );

		REQUIRE( exactlyEqual(mesh.cell(15).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(16).vertices(), Array4i{  8,  9, 13, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(17).vertices(), Array4i{  9, 10, 14, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(18).vertices(), Array4i{ 10, 11, 15, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(19).vertices(), Array2i{ 11, 15 }) );
		
		REQUIRE( exactlyEqual(mesh.cell(21).vertices(), Array2i{ 12, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(22).vertices(), Array2i{ 13, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(23).vertices(), Array2i{ 14, 15 }) );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( exactlyEqual(mesh.cell( 2).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell( 3).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell( 4).vertices(), Array2i{  2,  3 }) );

		REQUIRE( exactlyEqual(mesh.cell( 9).vertices(), Array2i{  0,  1 }) );
		REQUIRE( exactlyEqual(mesh.cell(10).vertices(), Array2i{  1,  2 }) );
		REQUIRE( exactlyEqual(mesh.cell(11).vertices(), Array2i{  2,  3 }) );
		
		REQUIRE( exactlyEqual(mesh.cell(14).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(15).vertices(), Array2i{  0,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(16).vertices(), Array4i{  0,  1,  5,  4 }) );
		REQUIRE( exactlyEqual(mesh.cell(17).vertices(), Array4i{  1,  2,  6,  5 }) );
		REQUIRE( exactlyEqual(mesh.cell(18).vertices(), Array4i{  2,  3,  7,  6 }) );
		REQUIRE( exactlyEqual(mesh.cell(19).vertices(), Array2i{  3,  7 }) );
		REQUIRE( exactlyEqual(mesh.cell(20).vertices(), Array2i{  3,  7 }) );

		REQUIRE( exactlyEqual(mesh.cell(21).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(22).vertices(), Array2i{  4,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(23).vertices(), Array4i{  4,  5,  9,  8 }) );
		REQUIRE( exactlyEqual(mesh.cell(24).vertices(), Array4i{  5,  6, 10,  9 }) );
		REQUIRE( exactlyEqual(mesh.cell(25).vertices(), Array4i{  6,  7, 11, 10 }) );
		REQUIRE( exactlyEqual(mesh.cell(26).vertices(), Array2i{  7, 11 }) );
		REQUIRE( exactlyEqual(mesh.cell(27).vertices(), Array2i{  7, 11 }) );

		REQUIRE( exactlyEqual(mesh.cell(28).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(29).vertices(), Array2i{  8, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(30).vertices(), Array4i{  8,  9, 13, 12 }) );
		REQUIRE( exactlyEqual(mesh.cell(31).vertices(), Array4i{  9, 10, 14, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(32).vertices(), Array4i{ 10, 11, 15, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(33).vertices(), Array2i{ 11, 15 }) );
		REQUIRE( exactlyEqual(mesh.cell(34).vertices(), Array2i{ 11, 15 }) );

		REQUIRE( exactlyEqual(mesh.cell(37).vertices(), Array2i{ 12, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(38).vertices(), Array2i{ 13, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(39).vertices(), Array2i{ 14, 15 }) );

		REQUIRE( exactlyEqual(mesh.cell(44).vertices(), Array2i{ 12, 13 }) );
		REQUIRE( exactlyEqual(mesh.cell(45).vertices(), Array2i{ 13, 14 }) );
		REQUIRE( exactlyEqual(mesh.cell(46).vertices(), Array2i{ 14, 15 }) );
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}

TEST_CASE(  "True structCell: edges" ){
	using hms::exactlyEqual;

	auto testOneLayer = [](const auto& mesh){
		REQUIRE( mesh.cell( 1).edge() == 0 );
		REQUIRE( mesh.cell( 2).edge() == 1 );
		REQUIRE( mesh.cell( 3).edge() == 2 );
		
		REQUIRE( mesh.cell( 5).edge() == 12);
		REQUIRE( exactlyEqual(mesh.cell( 6).edges(), Array4i{  0, 13,  3, 12 } ) );
		REQUIRE( exactlyEqual(mesh.cell( 7).edges(), Array4i{  1, 14,  4, 13 } ) );
		REQUIRE( exactlyEqual(mesh.cell( 8).edges(), Array4i{  2, 15,  5, 14 } ) );
		REQUIRE( mesh.cell( 9).edge() == 15);

		REQUIRE( mesh.cell(10).edge() == 16);
		REQUIRE( exactlyEqual(mesh.cell(11).edges(), Array4i{  3, 17,  6, 16 } ) );
		REQUIRE( exactlyEqual(mesh.cell(12).edges(), Array4i{  4, 18,  7, 17 } ) );
		REQUIRE( exactlyEqual(mesh.cell(13).edges(), Array4i{  5, 19,  8, 18 } ) );
		REQUIRE( mesh.cell(14).edge() == 19);

		REQUIRE( mesh.cell(15).edge() == 20);
		REQUIRE( exactlyEqual(mesh.cell(16).edges(), Array4i{  6, 21,  9, 20 } ) );
		REQUIRE( exactlyEqual(mesh.cell(17).edges(), Array4i{  7, 22, 10, 21 } ) );
		REQUIRE( exactlyEqual(mesh.cell(18).edges(), Array4i{  8, 23, 11, 22 } ) );
		REQUIRE( mesh.cell(19).edge() == 23);

		REQUIRE( mesh.cell(21).edge() ==  9 );
		REQUIRE( mesh.cell(22).edge() == 10 );
		REQUIRE( mesh.cell(23).edge() == 11 );
	};

	auto testTwoLayers = [](const auto& mesh){
		REQUIRE( mesh.cell( 2).edge() == 0 );
		REQUIRE( mesh.cell( 3).edge() == 1 );
		REQUIRE( mesh.cell( 4).edge() == 2 );
		
		REQUIRE( mesh.cell( 9).edge() == 0 );
		REQUIRE( mesh.cell(10).edge() == 1 );
		REQUIRE( mesh.cell(11).edge() == 2 );

		REQUIRE( mesh.cell(14).edge() == 12);
		REQUIRE( mesh.cell(15).edge() == 12);
		REQUIRE( exactlyEqual(mesh.cell(16).edges(), Array4i{  0, 13,  3, 12 } ) );
		REQUIRE( exactlyEqual(mesh.cell(17).edges(), Array4i{  1, 14,  4, 13 } ) );
		REQUIRE( exactlyEqual(mesh.cell(18).edges(), Array4i{  2, 15,  5, 14 } ) );
		REQUIRE( mesh.cell(19).edge() == 15);
		REQUIRE( mesh.cell(20).edge() == 15);

		REQUIRE( mesh.cell(21).edge() == 16);
		REQUIRE( mesh.cell(22).edge() == 16);
		REQUIRE( exactlyEqual(mesh.cell(23).edges(), Array4i{  3, 17,  6, 16 } ) );
		REQUIRE( exactlyEqual(mesh.cell(24).edges(), Array4i{  4, 18,  7, 17 } ) );
		REQUIRE( exactlyEqual(mesh.cell(25).edges(), Array4i{  5, 19,  8, 18 } ) );
		REQUIRE( mesh.cell(26).edge() == 19);
		REQUIRE( mesh.cell(27).edge() == 19);

		REQUIRE( mesh.cell(28).edge() == 20);
		REQUIRE( mesh.cell(29).edge() == 20);
		REQUIRE( exactlyEqual(mesh.cell(30).edges(), Array4i{  6, 21,  9, 20 } ) );
		REQUIRE( exactlyEqual(mesh.cell(31).edges(), Array4i{  7, 22, 10, 21 } ) );
		REQUIRE( exactlyEqual(mesh.cell(32).edges(), Array4i{  8, 23, 11, 22 } ) );
		REQUIRE( mesh.cell(33).edge() == 23);
		REQUIRE( mesh.cell(34).edge() == 23);

		REQUIRE( mesh.cell(37).edge() ==  9 );
		REQUIRE( mesh.cell(38).edge() == 10 );
		REQUIRE( mesh.cell(39).edge() == 11 );

		REQUIRE( mesh.cell(44).edge() ==  9 );
		REQUIRE( mesh.cell(45).edge() == 10 );
		REQUIRE( mesh.cell(46).edge() == 11 );
	};

	SECTION("One ghost layer"){
		testOneLayer(createStructMesh());
	}
}
